#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pthread.h>

#define WORKER_NUMBER 4
#define CAPACITY 3

struct task {
    int is_end;
    char path[128];
    char string[128];
};

typedef struct {
    int value;
    pthread_mutex_t mutex;
    pthread_cond_t cond;
} sema_t;

struct task buffer[CAPACITY];  // 任务队列
int in;
int out;

// 定义信号量：一个互斥，两个同步
sema_t mutex_sema;
sema_t empty_buffer_sema;
sema_t full_buffer_sema;



void put_item(struct task item) {
    buffer[in] = item;
    in = (in + 1) % CAPACITY;
}

void sema_init(sema_t *sema, int value) {
    sema->value = value;
    pthread_mutex_init(&sema->mutex, NULL);
    pthread_cond_init(&sema->cond, NULL);
}

void sema_wait(sema_t *sema) {
    pthread_mutex_lock(&sema->mutex);
    while (sema->value <= 0)
        pthread_cond_wait(&sema->cond, &sema->mutex);
    sema->value --;
    pthread_mutex_unlock(&sema->mutex);
}

void sema_signal(sema_t *sema) {
    pthread_mutex_lock(&sema->mutex);
    ++ sema->value;
    pthread_cond_signal(&sema->cond);
    pthread_mutex_unlock(&sema->mutex);
}

struct task get_item() {
    sema_wait(&full_buffer_sema);
    sema_wait(&mutex_sema);
    struct task item;

    item = buffer[out];
    out = (out + 1) % CAPACITY;
    sema_signal(&mutex_sema);
    sema_signal(&empty_buffer_sema);
    return item;
}

void put_queue(int is_end, char *path, char *string) {
    struct task tk;
    tk.is_end = is_end;
    strcpy(tk.path, path);
    strcpy(tk.string, string);

    sema_wait(&empty_buffer_sema);
    sema_wait(&mutex_sema);
    put_item(tk);
    sema_signal(&mutex_sema);
    sema_signal(&full_buffer_sema);
}

void find_file(char *path, char *target) {
    FILE *file = fopen(path, "r");

    char line[256];
    while(fgets(line, sizeof(line), file)) {
        if(strstr(line, target)) printf("%s: %s", path, line);
    }

    fclose(file);
}

void *worker_entry(void *arg) {
    struct task tk;
    while(1) {
        tk = get_item();
        if(tk.is_end) break;
        find_file(tk.path, tk.string);
    }
}

void find_dir(char *path, char *target) {
    DIR *dir = opendir(path);
    struct dirent *entry;
    while(entry = readdir(dir)) {
        if(strcmp(entry->d_name, ".") == 0) continue;
        if(strcmp(entry->d_name, "..") == 0) continue;
        char buf[1024];
        strcpy(buf, path);
        strcat(buf, "/");
        strcat(buf, entry->d_name);
        if(entry->d_type == DT_DIR) find_dir(buf, target);
        if(entry->d_type == DT_REG) put_queue(0, buf, target);
    }
    closedir(dir);
}

int main(int argc, char *argv[]) {
    if(argc != 3) {
        puts("Usage: sfind file string");
        return 0;
    }

    char *path = argv[1];
    char *string = argv[2];

    struct stat info;
    stat(path, &info);

    if(S_ISREG(info.st_mode)) {
        find_file(path, string);
        return 0;
    }

    sema_init(&mutex_sema, 1);
    sema_init(&empty_buffer_sema, CAPACITY - 1);
    sema_init(&full_buffer_sema, 0);

    pthread_t workers[WORKER_NUMBER];
    for(int i = 0; i < WORKER_NUMBER; i ++) 
        pthread_create(&workers[i], NULL, worker_entry, NULL);

    // 对目录 path 进行递归遍历: - 遇见叶子节点时 - 把叶子节点的路径加入到任务队列中(is_end 为假)
    find_dir(path, string);

    // 创建 WORER_NUMBER 个特殊任务(is_end 为真)，使子线程退出
    for(int i = 0; i < WORKER_NUMBER; i ++) put_queue(1, "", "");

    // 等待所有的子线程结束
    for(int i = 0; i < WORKER_NUMBER; i ++) 
        pthread_join(workers[i], NULL);

    return 0;
}
